package org.jvm.rtda.thread;

import org.jvm.instruction.references.ATHROW;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.KlassLoaderRegister;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.util.JavaCallUtil;

/**
 * java线程
 *
 * @author 海燕
 * @date 2023/1/12
 */
public class Thread {

    /**
     * 线程名
     */
    private String name;

    /**
     * program count 程序计数器
     */
    private int pc;

    /**
     * 线程方法栈
     */
    private Stack stack;

    public String getName() {
        return name;
    }

    public int getPc() {
        return pc;
    }

    public void setPc(int pc) {
        this.pc = pc;
    }

    public Thread(String name) {
        this.stack = new Stack(1024);
        this.name = name;
    }

    public void pushFrame(Frame frame) {
        stack.push(frame);
    }

    public Frame popFrame() {
        return stack.pop();
    }

    public Frame currentFrame() {
        return stack.top();
    }

    public void clearStack() {
        this.stack.clear();
    }

    /**
     * 根据方法信息生成新栈帧，并压栈
     *
     * @param method
     * @return
     */
    public Frame newFrameAndPush(Method method) {
        Frame frame = new Frame(this, method);
        pushFrame(frame);
        return frame;
    }

    public Stack getStack() {
        return stack;
    }

    public void setStack(Stack stack) {
        this.stack = stack;
    }

    public void throwNullPointerException() {
        Klass npeKlass = KlassLoaderRegister.loadKlass("java/lang/NullPointerException", this);
        Object npeRef = npeKlass.newObject();
        JavaCallUtil.javaCall(this,npeKlass.getInstanceMethod("<init>","()V"),npeRef);
        threadThrow(npeRef);
    }

    public void throwArrayIndexOutOfBoundsException() {
        Klass npeKlass = KlassLoaderRegister.loadKlass("java/lang/ArrayIndexOutOfBoundsException", this);
        Object npeRef = npeKlass.newObject();
        JavaCallUtil.javaCall(this,npeKlass.getInstanceMethod("<init>","()V"),npeRef);
        threadThrow(npeRef);
    }

    /**
     * 线程抛出
     *
     * @param throwable
     */
    public void threadThrow(Object throwable) {
        ATHROW.throwException(throwable, this);
    }
}
